/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-04-19     sethnie       the first version
 *
 * 连线：
 CH32V307     TFT-LCD   FSMC
    PD11 --  -- FSMC_A16
    PD12 --  -- FSMC_A17
    PD5  --  -- FSMC_NEW
    PD4  --  -- FSMC_NOE
    PA14 --  -- LCDRST
    PD14 --  -- FSMC_D0
    PD15 --  -- FSMC_D1
    PD0  --  -- FSMC_D2
    PD1  --  -- FSMC_D3
    PE7  --  -- FSMC_D4
    PE8  --  -- FSMC_D5
    PE9  --  -- FSMC_D6
    PE10 --  -- FSMC_D7
    PE11 --  -- FSMC_D8
    PE12 --  -- FSMC_D9
    PE13 --  -- FSMC_D10
    PE14 --  -- FSMC_D11
    PE15 --  -- FSMC_D12
    PD8  --  -- FSMC_D13
    PD9  --  -- FSMC_D14
    PD10 --  -- FSMC_D15
    PB14 -- BL -- BL
    PA8  --  -- MISO_NC
    PB3  --  -- MOSI_SDA
    PB15 --  -- TKINT
    PC13 --  -- BUSY_NC
    PC0  --  -- TKRST#
    PB4  --  -- CLK
 */

#include <drv_lcd.h>
#include "stdlib.h"
#include "font.h"


/* LCD brush and background colors */
rt_uint16_t POINT_COLOR=0x0000;
rt_uint16_t BACK_COLOR=0xFFFF;

_lcd_dev lcddev;

/*********************************************************************
 * @fn      LCD_WR_REG
 *
 * @brief   Write register
 *
 * @param   regval - register value
 *
 * @return  none
 */
void LCD_WR_REG(rt_uint16_t regval)
{
    LCD->LCD_REG=regval;
}

/*********************************************************************
 * @fn      LCD_WR_DATA
 *
 * @brief   Write data
 *
 * @param   data
 *
 * @return  none
 */
void LCD_WR_DATA(rt_uint16_t data)
{
    LCD->LCD_RAM=data;
}

/*********************************************************************
 * @fn      LCD_RD_DATA
 *
 * @brief   Read data
 *
 * @return  ram - read data
 */
rt_uint16_t LCD_RD_DATA(void)
{
    vu16 ram;
    ram=LCD->LCD_RAM;
    return ram;
}

/*********************************************************************
 * @fn      LCD_WriteReg
 *
 * @brief   Write value to register
 *
 * @param   LCD_Reg - register addr
 *          LCD_RegValue - value
 *
 * @return  none
 */
void LCD_WriteReg(rt_uint16_t LCD_Reg,rt_uint16_t LCD_RegValue)
{
    LCD->LCD_REG = LCD_Reg;
    LCD->LCD_RAM = LCD_RegValue;
}

/*********************************************************************
 * @fn      LCD_ReadReg
 *
 * @brief   Read value from register
 *
 * @param   LCD_Reg - register addr
 *
 * @return  register value
 */
rt_uint16_t LCD_ReadReg(rt_uint16_t LCD_Reg)
{
    LCD_WR_REG(LCD_Reg);
    rt_thread_delay(5);
    return LCD_RD_DATA();
}

/*********************************************************************
 * @fn      LCD_WriteRAM_Prepare
 *
 * @brief   Write GRAM prepare
 *
 * @return  none
 */
void LCD_WriteRAM_Prepare(void)
{
    LCD->LCD_REG=lcddev.wramcmd;
}

/*********************************************************************
 * @fn      LCD_WriteRAM
 *
 * @brief   Write GRAM
 *
 * @param   RGB_Code - colour value
 *
 * @return  none
 */
void LCD_WriteRAM(rt_uint16_t RGB_Code)
{
    LCD->LCD_RAM = RGB_Code;
}

/*********************************************************************
 * @fn      LCD_SetCursor
 *
 * @brief   set Cursor
 *
 * @param   Xpos - Abscissa
 *          Ypos - Ordinate
 *
 * @return  none
 */
void LCD_SetCursor(rt_uint16_t Xpos, rt_uint16_t Ypos)
{
    if(lcddev.id==0X9341)
    {
        LCD_WR_REG(lcddev.setxcmd);
        LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);
        LCD_WR_REG(lcddev.setycmd);
        LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF);
    }
}

/*********************************************************************
 * @fn      LCD_Scan_Dir
 *
 * @brief   set scan direction
 *
 * @param   dir - direction
 *
 * @return  none
 */
void LCD_Scan_Dir(rt_uint8_t dir)
{
    rt_uint16_t regval=0;
    rt_uint16_t dirreg=0;
    rt_uint16_t temp;

    if((lcddev.dir==1&&lcddev.id==0x9341)){
        switch(dir)
        {
            case 0:dir=6;break;
            case 1:dir=7;break;
            case 2:dir=4;break;
            case 3:dir=5;break;
            case 4:dir=1;break;
            case 5:dir=0;break;
            case 6:dir=3;break;
            case 7:dir=2;break;
        }
    }

    if(lcddev.id==0x9341)
    {
        switch(dir)
        {
            case L2R_U2D:
                regval|=(0<<7)|(0<<6)|(0<<5);
                break;
            case L2R_D2U:
                regval|=(1<<7)|(0<<6)|(0<<5);
                break;
            case R2L_U2D:
                regval|=(0<<7)|(1<<6)|(0<<5);
                break;
            case R2L_D2U:
                regval|=(1<<7)|(1<<6)|(0<<5);
                break;
            case U2D_L2R:
                regval|=(0<<7)|(0<<6)|(1<<5);
                break;
            case U2D_R2L:
                regval|=(0<<7)|(1<<6)|(1<<5);
                break;
            case D2U_L2R:
                regval|=(1<<7)|(0<<6)|(1<<5);
                break;
            case D2U_R2L:
                regval|=(1<<7)|(1<<6)|(1<<5);
                break;
        }
        dirreg=0X36;
        regval|=0X08;
        LCD_WriteReg(dirreg,regval);
        if(lcddev.id!=0X1963)
        {
            if(regval&0X20)
            {
                if(lcddev.width<lcddev.height)
                {
                    temp=lcddev.width;
                    lcddev.width=lcddev.height;
                    lcddev.height=temp;
                }
            }else
            {
                if(lcddev.width>lcddev.height)
                {
                    temp=lcddev.width;
                    lcddev.width=lcddev.height;
                    lcddev.height=temp;
                }
            }
        }

        LCD_WR_REG(lcddev.setxcmd);
        LCD_WR_DATA(0);LCD_WR_DATA(0);
        LCD_WR_DATA((lcddev.width-1)>>8);LCD_WR_DATA((lcddev.width-1)&0XFF);
        LCD_WR_REG(lcddev.setycmd);
        LCD_WR_DATA(0);LCD_WR_DATA(0);
        LCD_WR_DATA((lcddev.height-1)>>8);LCD_WR_DATA((lcddev.height-1)&0XFF);

    }
}

/*********************************************************************
 * @fn      LCD_Display_Dir
 *
 * @brief   set display direction
 *
 * @param   dir - direction
 *
 * @return  none
 */
void LCD_Display_Dir(rt_uint8_t dir)
{
    if(dir==0)
    {
        lcddev.dir=0;
        lcddev.width=240;
        lcddev.height=320;
        if(lcddev.id==0X9341)
        {
            lcddev.wramcmd=0X2C;
            lcddev.setxcmd=0X2A;
            lcddev.setycmd=0X2B;
        }

    }else
    {
        lcddev.dir=1;
        lcddev.width=320;
        lcddev.height=240;
        if(lcddev.id==0X9341)
        {
            lcddev.wramcmd=0X2C;
            lcddev.setxcmd=0X2A;
            lcddev.setycmd=0X2B;
        }

    }
    LCD_Scan_Dir(DFT_SCAN_DIR);
}

/*********************************************************************
 * @fn      LCD_Init
 *
 * @brief   Init LCD
 *
 * @return  none
 */
void LCD_Reset_GPIO_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure={0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_14);

    //LCD reset
    GPIO_ResetBits(GPIOA,GPIO_Pin_14);
    rt_thread_mdelay(100);
    GPIO_SetBits(GPIOA,GPIO_Pin_14);
}
void LCD_Init(void)
{
    // lcd reset
    LCD_Reset_GPIO_Init();

    GPIO_InitTypeDef GPIO_InitStructure;
    FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
    FSMC_NORSRAMTimingInitTypeDef  readWriteTiming;
    FSMC_NORSRAMTimingInitTypeDef  writeTiming;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE,ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    /*   RS  PG0    PD12  */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* CS: PG12    PD11*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOD,GPIO_Pin_11);

    readWriteTiming.FSMC_AddressSetupTime = 0x01;
    readWriteTiming.FSMC_AddressHoldTime = 0x00;
    readWriteTiming.FSMC_DataSetupTime = 0x0f;
    readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
    readWriteTiming.FSMC_CLKDivision = 0x00;
    readWriteTiming.FSMC_DataLatency = 0x00;
    readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;

    writeTiming.FSMC_AddressSetupTime = 0x00;
    writeTiming.FSMC_AddressHoldTime = 0x00;
    writeTiming.FSMC_DataSetupTime = 0x03;
    writeTiming.FSMC_BusTurnAroundDuration = 0x00;
    writeTiming.FSMC_CLKDivision = 0x00;
    writeTiming.FSMC_DataLatency = 0x00;
    writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;

    FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;

    FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
    FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;
    FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
    FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
    FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable;
    FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
    FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming;

    FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);

    FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);

    rt_thread_mdelay(50);
    lcddev.id=LCD_ReadReg(0x0000);
    if(lcddev.id<0XFF||lcddev.id==0XFFFF||lcddev.id==0X9300)
    {
        LCD_WR_REG(0XD3);
        lcddev.id=LCD_RD_DATA();    //dummy read
        lcddev.id=LCD_RD_DATA();
        lcddev.id=LCD_RD_DATA();
        lcddev.id<<=8;
        lcddev.id|=LCD_RD_DATA();
    }

    rt_kprintf(" LCD ID:%x\r\n",lcddev.id);
    lcddev.id=0X9341;

    if(lcddev.id==0X9341)
    {
        LCD_WR_REG(0xCF);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0xC1);
        LCD_WR_DATA(0X30);
        LCD_WR_REG(0xED);
        LCD_WR_DATA(0x64);
        LCD_WR_DATA(0x03);
        LCD_WR_DATA(0X12);
        LCD_WR_DATA(0X81);
        LCD_WR_REG(0xE8);
        LCD_WR_DATA(0x85);
        LCD_WR_DATA(0x10);
        LCD_WR_DATA(0x7A);
        LCD_WR_REG(0xCB);
        LCD_WR_DATA(0x39);
        LCD_WR_DATA(0x2C);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x34);
        LCD_WR_DATA(0x02);
        LCD_WR_REG(0xF7);
        LCD_WR_DATA(0x20);
        LCD_WR_REG(0xEA);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_REG(0xC0);    //Power control
        LCD_WR_DATA(0x1B);   //VRH[5:0]
        LCD_WR_REG(0xC1);    //Power control
        LCD_WR_DATA(0x01);   //SAP[2:0];BT[3:0]
        LCD_WR_REG(0xC5);    //VCM control
        LCD_WR_DATA(0x30);   //3F
        LCD_WR_DATA(0x30);   //3C
        LCD_WR_REG(0xC7);    //VCM control2
        LCD_WR_DATA(0XB7);
        LCD_WR_REG(0x36);    // Memory Access Control
        LCD_WR_DATA(0x48);
        LCD_WR_REG(0x3A);
        LCD_WR_DATA(0x55);
        LCD_WR_REG(0xB1);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x1A);
        LCD_WR_REG(0xB6);    // Display Function Control
        LCD_WR_DATA(0x0A);
        LCD_WR_DATA(0xA2);
        LCD_WR_REG(0xF2);    // 3Gamma Function Disable
        LCD_WR_DATA(0x00);
        LCD_WR_REG(0x26);    //Gamma curve selected
        LCD_WR_DATA(0x01);
        LCD_WR_REG(0xE0);    //Set Gamma
        LCD_WR_DATA(0x0F);
        LCD_WR_DATA(0x2A);
        LCD_WR_DATA(0x28);
        LCD_WR_DATA(0x08);
        LCD_WR_DATA(0x0E);
        LCD_WR_DATA(0x08);
        LCD_WR_DATA(0x54);
        LCD_WR_DATA(0XA9);
        LCD_WR_DATA(0x43);
        LCD_WR_DATA(0x0A);
        LCD_WR_DATA(0x0F);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_REG(0XE1);    //Set Gamma
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x15);
        LCD_WR_DATA(0x17);
        LCD_WR_DATA(0x07);
        LCD_WR_DATA(0x11);
        LCD_WR_DATA(0x06);
        LCD_WR_DATA(0x2B);
        LCD_WR_DATA(0x56);
        LCD_WR_DATA(0x3C);
        LCD_WR_DATA(0x05);
        LCD_WR_DATA(0x10);
        LCD_WR_DATA(0x0F);
        LCD_WR_DATA(0x3F);
        LCD_WR_DATA(0x3F);
        LCD_WR_DATA(0x0F);
        LCD_WR_REG(0x2B);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x01);
        LCD_WR_DATA(0x3f);
        LCD_WR_REG(0x2A);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0x00);
        LCD_WR_DATA(0xef);
        LCD_WR_REG(0x11); //Exit Sleep
        rt_thread_mdelay(120);
        LCD_WR_REG(0x29); //display on
    }

    LCD_Display_Dir(0);
    GPIO_SetBits(GPIOB,GPIO_Pin_14);
    LCD_Clear(WHITE);
}

/*********************************************************************
 * @fn      LCD_Clear
 *
 * @brief   Clear screen
 *
 * @param   color - fill color
 *
 * @return  none
 */
void LCD_Clear(rt_uint16_t color)
{
    rt_uint32_t index=0;
    rt_uint32_t totalpoint=lcddev.width;
    totalpoint*=lcddev.height;
    LCD_SetCursor(0x00,0x0000);
    LCD_WriteRAM_Prepare();
    for(index=0;index<totalpoint;index++)
    {
        LCD->LCD_RAM=color;
    }
}

/*********************************************************************
 * @fn      LCD_ShowString
 *
 * @brief   Display string
 *
 * @param   x - X coordinate
 *          y - Y coordinate
 *          width - String width
 *          height - String height
 *          size - font size
 *          *p - String start address
 *
 * @return  none
 */
void LCD_ShowString(rt_uint16_t x,rt_uint16_t y,rt_uint16_t width,rt_uint16_t height,rt_uint8_t size,rt_uint8_t *p)
{
    rt_uint8_t x0=x;
    width+=x;
    height+=y;
    while((*p<='~')&&(*p>=' '))
    {
        if(x>=width){x=x0;y+=size;}
        if(y>=height)break;
        LCD_ShowChar(x,y,*p,size,0);
        x+=size/2;
        p++;
    }
}

/*********************************************************************
 * @fn      LCD_Pow
 *
 * @brief   m^n function
 *
 * @return  result - m^n
 */
rt_uint32_t LCD_Pow(rt_uint8_t m,rt_uint8_t n)
{
    rt_uint32_t result=1;
    while(n--)result*=m;
    return result;
}

/*********************************************************************
 * @fn      LCD_ShowNum
 *
 * @brief   Display number
 *
 * @param   x - X coordinate
 *          y - Y coordinate
 *          len - number lenth
 *          size -font size
 *          color - font color
 *          num - value(0~4294967295)
 *
 * @return  none
 */
void LCD_ShowNum(rt_uint16_t x,rt_uint16_t y,rt_uint32_t num,rt_uint8_t len,rt_uint8_t size)
{
    rt_uint8_t t,temp;
    rt_uint8_t enshow=0;
    for(t=0;t<len;t++)
    {
        temp=(num/LCD_Pow(10,len-t-1))%10;
        if(enshow==0&&t<(len-1))
        {
            if(temp==0)
            {
                LCD_ShowChar(x+(size/2)*t,y,' ',size,0);
                continue;
            }else enshow=1;

        }
        LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,0);
    }
}

/*********************************************************************
 * @fn      LCD_ShowChar
 *
 * @brief   Displays a character in the specified position
 *
 * @param   x - X coordinate
 *          y - Y coordinate
 *          num - Displays a character
 *          size -font size
 *          mode - superposition mode
 *
 * @return  none
 */
void LCD_ShowChar(rt_uint16_t x,rt_uint16_t y,rt_uint8_t num,rt_uint8_t size,rt_uint8_t mode)
{
    rt_uint8_t temp,t1,t;
    rt_uint16_t y0=y;
    rt_uint8_t csize=(size/8+((size%8)?1:0))*(size/2);
    num=num-' ';
    for(t=0;t<csize;t++)
    {
        if(size==12)temp=asc2_1206[num][t];
        else if(size==16)temp=asc2_1608[num][t];
        else if(size==24)temp=asc2_2412[num][t];
        else return;
        for(t1=0;t1<8;t1++)
        {
            if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
            else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
            temp<<=1;
            y++;
            if(y>=lcddev.height)return;
            if((y-y0)==size)
            {
                y=y0;
                x++;
                if(x>=lcddev.width)return;
                break;
            }
        }
    }
}

/*********************************************************************
 * @fn      LCD_Fast_DrawPoint
 *
 * @brief   fast draw point
 *
 * @param   x - X coordinate
 *          y - Y coordinate
 *          color - font color
 *
 * @return  none
 */
void LCD_Fast_DrawPoint(rt_uint16_t x,rt_uint16_t y,rt_uint16_t color)
{
    if(lcddev.id==0X9341)
    {
        LCD_WR_REG(lcddev.setxcmd);
        LCD_WR_DATA(x>>8);LCD_WR_DATA(x&0XFF);
        LCD_WR_REG(lcddev.setycmd);
        LCD_WR_DATA(y>>8);LCD_WR_DATA(y&0XFF);
    }
    LCD->LCD_REG=lcddev.wramcmd;
    LCD->LCD_RAM=color;
}
